﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Agility.TestProject.AppDriver.UIAutomation;
using Agility.Wcf.ClientProxy;
using Xl = Microsoft.Office.Interop.Excel;

namespace Agility.TestProject.AppDriver
{
    public class AgilityExcelDriver : AppDriverBase
    {
        private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        private AgilityExcelUI uiAutomation;
        private Xl.Application excel;

        public override void Cleanup()
        {
            logger.Debug("--> Entering Cleanup");
            if (this.excel != null)
            {
                this.excel.ActiveWorkbook.Close(false);
            }
            if (this.uiAutomation != null)
            {
                this.uiAutomation.Dispose(); 
            }
            if (this.excel != null)
            {
                this.excel.Quit();
                System.Runtime.InteropServices.Marshal.ReleaseComObject(this.excel);
            }
            base.Cleanup();
            logger.Debug("<-- Leaving Cleanup");
        }

        public void StartExcel()
        {
            logger.Debug("--> Entering StartExcel");
            this.excel = new Xl.Application();
            this.excel.Visible = true;
            Xl.Workbook wb = this.excel.Workbooks.Add();
            logger.Debug("Started Excel");
            this.uiAutomation = new AgilityExcelUI(string.Format(System.Globalization.CultureInfo.CurrentUICulture, "{0} - {1}", this.excel.Name, this.excel.Windows[1].Caption));
            logger.Debug("Created the UI automation layer");
            logger.Debug("<-- Leaving StartExcel");
        }

        public void EnsureAddInIsInstalledAndActivated()
        {
            logger.Debug("--> Entering EnsureAddInIsInstalledAndActivated");
            Microsoft.Office.Core.COMAddIn addIn;
            this.excel = new Xl.Application();
            this.excel.Visible = false;
            try
            {
                addIn = this.excel.COMAddIns.Item("Agility.Office.Excel2010AddIn");
                logger.Debug("AddIn is installed");
            }
            catch (Exception e)
            {
                logger.Fatal(e.ToString());
                // TODO: implement me   
                throw;
            }
            if (!addIn.Connect)
            {
                addIn.Connect = true;
            }
            logger.Debug("AddIn is activated");
            this.excel.DisplayAlerts = false;
            this.excel.Quit();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(this.excel);
            logger.Debug("<-- Leaving EnsureAddInIsInstalledAndActivated");
        }

        public void SetKeepMeLoggedOn(bool enabled)
        {
            logger.Debug("--> Entering EnableKeepMeLoggedIn");
            string valueText;
            if (enabled)
            {
                valueText = "True";
            }
            else
            {
                valueText = "False";
            }
            string userConfig = this.GetUserConfigPath("Excel");
            System.Xml.XmlDocument userSettings = new System.Xml.XmlDocument();
            userSettings.Load(userConfig);
            logger.Debug("loaded user settings for Excel-AddIn");
            System.Xml.XmlNode nodeKeepMeLoggedOn = userSettings.DocumentElement.SelectSingleNode("descendant::userSettings/Agility.Office.ExcelAddIn.Properties.Settings/setting[@name=\"KeepMeLoggedOn\"]");
            if (nodeKeepMeLoggedOn == null)
            {
                // in case the settings node for KeepMeLoggedon is not yet created add it to the user.settings file
                System.Xml.XmlElement setting = userSettings.CreateElement("setting");
                System.Xml.XmlAttribute attributeName = userSettings.CreateAttribute("name");
                attributeName.Value = "KeepMeLoggedOn";
                setting.Attributes.Append(attributeName);
                System.Xml.XmlAttribute attributeSerialzeAs = userSettings.CreateAttribute("serializeAs");
                attributeSerialzeAs.Value = "String";
                setting.Attributes.Append(attributeSerialzeAs);
                System.Xml.XmlElement valueElement = userSettings.CreateElement("value");
                setting.AppendChild(valueElement);
                userSettings.DocumentElement.SelectSingleNode("descendant::userSettings/Agility.Office.ExcelAddIn.Properties.Settings").AppendChild(setting);
                nodeKeepMeLoggedOn = userSettings.DocumentElement.SelectSingleNode("descendant::userSettings/Agility.Office.ExcelAddIn.Properties.Settings/setting[@name=\"KeepMeLoggedOn\"]");
            }
            nodeKeepMeLoggedOn.FirstChild.InnerText = valueText;
            userSettings.Save(userConfig);
            logger.Debug("Enabled Keep-Me-Logged-On in user settings for Excel-AddIn");
            logger.Debug("--> Entering EnableKeepMeLoggedIn");
        }

        public void SimulatePreviousLogOn()
        {
            logger.Debug("--> Entering SimulatePreviousLogOn");
            AgilityClientProxy proxy = AgilityClientProxy.GetInstance(new Uri("http://localhost:52878/"));
            proxy.KeepMeLoggedOn = true;
            proxy.LogOn(this.UserName, "Test##100");
            logger.Debug("Log on with user {0} {1}", this.UserName, proxy.IsLoggedOn == true ? "worked" : "failed");
            proxy.Dispose();
            logger.Debug("Disposed of AgilityClientProxy thereby saving the cookie");

            System.Xml.XmlDocument userSettings = new System.Xml.XmlDocument();

            // user settings are created per application running the code. Here the first logon attempt is performed by NUnit running an instance of AgilityClientProxy.
            // therefore the user.settings are stored in NUnit's %AppData%/Local subfolder. The subsequent logon is performed from within Excel. Thus we need to copy
            // the cookie and username from one settings file to the other.

            // reading the cookie from the user.config created by NUnit
            string nunitConfic = this.GetUserConfigPath("Nunit");
            userSettings.Load(nunitConfic);
            string cookie = userSettings.DocumentElement.SelectSingleNode("descendant::userSettings/Agility.Wcf.ClientProxy.Properties.Settings/setting[@name=\"Cookie\"]").FirstChild.InnerText;
            
            // saving the cooke to the user.config file used by Excel
            string excelConfig = this.GetUserConfigPath("Excel");
            userSettings.Load(excelConfig);
            userSettings.DocumentElement.SelectSingleNode("descendant::userSettings/Agility.Wcf.ClientProxy.Properties.Settings/setting[@name=\"Cookie\"]").FirstChild.InnerText = cookie;
            userSettings.DocumentElement.SelectSingleNode("descendant::userSettings/Agility.Office.ExcelAddIn.Properties.Settings/setting[@name=\"UserName\"]").FirstChild.InnerText = this.UserName;
            userSettings.Save(excelConfig);
            logger.Debug("Copied the cookie from the previous logon into Excel's user.config");
            logger.Debug("<-- Leaving SimulatePreviousLogOn");
        }

        public void FillInLoginData()
        {
            logger.Debug("--> Entering FillInLoginData");
            try
            {
                this.uiAutomation.LoginSettingsDialog.TimeOut = 1000;
                this.uiAutomation.LoginSettingsDialog.WaitForVisible();
                logger.Debug("SettingsDialog is visible");
                this.uiAutomation.TextboxUserName.EnterText(this.UserName);
                logger.Debug("Entered Username");
                this.uiAutomation.TextboxPassword.EnterText("Test##100");
                logger.Debug("Entered Password");

            }
            catch (Exception e)
            {
                logger.Fatal(e.ToString());
                throw;
            }
            logger.Debug("<-- Leavinging FillInLoginData");
        }

        public void ConfirmSettingsDialog()
        {
            logger.Debug("--> Entering ConfirmSettingsDialog");
            this.uiAutomation.ButtonSettingsOk.Click();
            logger.Debug("<-- Leaving ConfirmSettingsDialog");
        }

        private string GetUserConfigPath(string application)
        {
            logger.Debug("--> Entering GetUserConfigPath");
            StringBuilder userConfigPath = new StringBuilder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
            System.IO.DirectoryInfo folder;
            System.IO.DirectoryInfo[] subfolders;
            switch (application)
            {
                case "Excel":
                    {
                        userConfigPath.Append("\\Microsoft_Corporation\\");
                        folder = new System.IO.DirectoryInfo(userConfigPath.ToString());
                        subfolders = folder.GetDirectories("Agility.Office.Excel*");
                        userConfigPath.Append(subfolders[0]).Append("\\");
                        break;
                    }
                case "Nunit":
                    {
                        userConfigPath.Append("\\NUnit.org\\");
                        folder = new System.IO.DirectoryInfo(userConfigPath.ToString());
                        subfolders = folder.GetDirectories("test*");
                        userConfigPath.Append(subfolders[0]).Append("\\");
                        break;
                    }
                default:
                    break;
            }
            folder = new System.IO.DirectoryInfo(userConfigPath.ToString());
            subfolders = folder.GetDirectories();
            subfolders.OrderBy(sf => sf.CreationTime);
            userConfigPath.Append(subfolders.Last()).Append("\\user.config");
            logger.Debug("<-- Leaving GetUserConfigPath");
            return userConfigPath.ToString();
        }

        public void DeleteUserSettings()
        {
            logger.Debug("--> Entering DeleteUserSettings");
            System.IO.FileInfo settingsFile = new System.IO.FileInfo(this.GetUserConfigPath("Excel"));
            settingsFile.Delete();
            logger.Debug("user.config deleted");
            logger.Debug("<-- Leaving DeleteUserSettings");
        }

        public void Logon()
        {
            logger.Debug("--> Entering Logon");
            this.uiAutomation.ButtonLogon.Click();
            logger.Debug("<-- Leaving Logon");
        }

        public void OpenAgilityTab()
        {
            logger.Debug("--> Entering OpenAgilityTab");
            this.uiAutomation.PageTabAgility.Click();
            logger.Debug("<-- Leaving OpenAgilityTab");
        }

        public bool VerifyLogon()
        {
            logger.Debug("--> Entering VerifyLogon");
            if (!this.uiAutomation.ButtonLogon.IsValid)
            {
                this.uiAutomation.ButtonLogon.Reattach();
            }
            // NOTE: uncomment the following line to enable hightlighting for the logon button
            // this.uiAutomation.ButtonLogon.Highlight();
            logger.Debug("<-- Leaving VerifyLogon");
            return !this.uiAutomation.ButtonLogon.IsEnabled;
        }

        /// <summary>
        /// Ensures the user is the role passed in. Assigns the role to the user if not already assigned.
        /// </summary>
        /// <param name="role">the role to be verified</param>
        public void EnsureRoleAssignment(string role)
        {
            logger.Debug("--> Entering EnsureRoleAssignmentAndLogin");
            if (!this.IsInRole(role))
            {
                this.AssignRole(role);
                logger.Debug("Assigned role");
                logger.Trace("\t{0}", role);
            }
            logger.Debug("Verified user is in role");
            logger.Debug("<-- Leaving EnsureRoleAssignmentAndLogin");
        }

        public void GetMyIdeas()
        {
            logger.Debug("--> Entering GetMyIdeas");
            this.uiAutomation.ButtonMyIdeas.Click();
            logger.Debug("<-- Leaving GetMyIdeas");
        }

        public bool VerifyAllMyIdeasDisplayed()
        {
            logger.Debug("--> Entering VerifyAllMyIdeasDisplayed");
            Xl.ListObject tblMyIdeas;
            try
            {
                tblMyIdeas = this.excel.ActiveSheet.ListObjects.Item["TableMyIdeas"];
            }
            catch (System.Runtime.InteropServices.COMException)
            {
                logger.Debug("Table doesn't exist");
                logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                logger.Trace("    returning false");
                return false;
            }
            List<Agility.Web.Models.DomainModel.Epic> expectedIdeas = this.TestEpics.Where(e => e.RequestedBy == this.UserName).ToList();
            if (tblMyIdeas.ListRows.Count != expectedIdeas.Count())
            {
                logger.Debug("Number of ideas doesn't match");
                logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                logger.Trace("    returning false");
                return false;
            }
            expectedIdeas.OrderBy(e => e.CreationDate).ThenBy(e => e.Title);
            for (int i = 0; i < tblMyIdeas.ListRows.Count; i++)
            {
                if (!expectedIdeas[i].Id.ToString().Equals(tblMyIdeas.ListRows[i+1].Range[1, 1].Text))
                {
                    logger.Debug("Ideas are not sorted correctly");
                    logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                    logger.Trace("    returning false");
                    return false;
                }
            }
            logger.Debug("<-- Leaving VerifyAllMyIdeasDisplayed");
            logger.Trace("    returning true");
            return true;
        }

        public bool VerifyAllMyIdeasDetailsDisplayed()
        {
            logger.Debug("--> Entering VerifyAllMyIdeasDetailsDisplayed");
            Xl.Worksheet activeSheet = this.excel.ActiveWorkbook.ActiveSheet;
            Xl.ListObject tblMyIdeas;
            try
            {
                tblMyIdeas = activeSheet.ListObjects.Item["TableMyIdeas"];
            }
            catch (System.Runtime.InteropServices.COMException)
            {
                logger.Debug("Table doesn't exist");
                logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                logger.Trace("    returning false");
                return false;
            }
            List<Agility.Web.Models.DomainModel.Epic> expectedIdeas = this.TestEpics.Where(e => e.RequestedBy == this.UserName).ToList();
            for (int i = 0; i < expectedIdeas.Count; i++)
            {
                // compare the title of the idea in the list of expected ideas to the title of the idea in the table
                if (!expectedIdeas[i].Title.Equals(tblMyIdeas.ListRows[i+1].Range[1,2].Text))
                {
                    logger.Debug("Title doesn't match");
                    logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                    logger.Trace("    returning false");
                    return false;
                }
                // compare the description of the idea in the list of expected ideas to the description of the idea in the table
                if (!expectedIdeas[i].Description.Equals(tblMyIdeas.ListRows[i+1].Range[1,3].Text))
                {
                    logger.Debug("Description doesn't match");
                    logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                    logger.Trace("    returning false");
                    return false;
                }
                // compare the date of submission of the idea in the list of expected ideas to the date of submission of the idea in the table
                System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo(1033);
                Microsoft.Office.Core.LanguageSettings lang = excel.LanguageSettings;
                logger.Debug("Excel language is {0}", lang.LanguageID[Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI]);
                culture = new System.Globalization.CultureInfo(lang.LanguageID[Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI]);
                DateTime creationDate = DateTime.Parse(tblMyIdeas.ListRows[i + 1].Range[1, 4].Text, culture);
                if (expectedIdeas[i].CreationDate != creationDate)
                {
                    logger.Debug("Creationdate doesn't match");
                    logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
                    logger.Trace("    returning false");
                    return false;
                }
                // TODO: Add verification for approval status
            }
            logger.Debug("<-- Leaving VerifyAllMyIdeasDetailsDisplayed");
            logger.Trace("    returning true");
            return true;
        }

        public bool VerifyColumnIsReadOnly(string columnName)
        {
            logger.Debug("--> Entering VerifyColumnIsReadOnly");
            Xl.ListObject tblMyIdeas = this.excel.ActiveSheet.ListObjects.Item["TableMyIdeas"];
            bool readOnly = !tblMyIdeas.ListColumns.Item["Id"].DataBodyRange.AllowEdit;
            bool protectedWs = this.excel.ActiveSheet.ProtectContents;
            bool hidden = tblMyIdeas.ListColumns.Item["Id"].Range.EntireColumn.Hidden;
            if (readOnly && protectedWs && hidden)
            {
                logger.Debug("Verification passed");
                logger.Debug("<-- Leaving VerifyColumnIsReadOnly");
                return true;
            }
            else
            {
                logger.Debug("Verification failed");
                logger.Trace("Cells are {0}readonly, worksheet is {1}protected, column is {2}hidden", readOnly == true ? " " : "not ", 
                                                                                   protectedWs == true ? " " : "not ",
                                                                                   hidden == true ? " " : "not ");
                logger.Debug("<-- Leaving VerifyColumnIsReadOnly");
                return false;
            }
        }

        public void AddNewIdea()
        {
            logger.Debug("--> Entering AddNewIdea");
            try
            {
                Xl.ListObject tblMyIdeas = this.excel.ActiveSheet.ListObjects.Item["TableMyIdeas"];
                tblMyIdeas.ListRows.AddEx();
            }
            catch (System.Runtime.InteropServices.COMException)
            {
                logger.Error("Table does not exist");
                NUnit.Framework.Assert.Fail("Table does not exist on Worksheet");
            } 
            logger.Debug("<-- Leaving AddNewIdea");
        }

        internal void EnterIdeaDetails()
        {
            logger.Debug("--> Entering EnterIdeaDetails");
            try
            {
                Xl.ListObject tblMyIdeas = this.excel.ActiveSheet.ListObjects.Item["TableMyIdeas"];
                int row = 0;
                int colTitle = tblMyIdeas.ListColumns["Title"].Index;
                int colDescription = tblMyIdeas.ListColumns["Description"].Index;
                foreach (Xl.Range id in tblMyIdeas.ListColumns["Id"].DataBodyRange.Rows)
                {
                    if (string.IsNullOrEmpty(id.Text))
                    {
                        row = id.Row;
                        logger.Debug("Found the row that was added");
                        break;
                    }
                }
                if (row==0)
                {
                    logger.Error("Couldn't find row of new idea");
                    NUnit.Framework.Assert.Fail("Couldn't find row of new idea");
                }
                tblMyIdeas.DataBodyRange[row, colTitle].Text = "Title of my new idea";
                tblMyIdeas.DataBodyRange[row, colDescription].Text = "This is the description of my new idea";
                logger.Debug("Added title and description");
            }
            catch (System.Runtime.InteropServices.COMException)
            {
                logger.Error("Table does not exist");
                NUnit.Framework.Assert.Fail("Table does not exist on Worksheet");
            } 
            logger.Debug("<-- Leaving EnterIdeaDetails");
        }

        internal void PublishChanges()
        {
            throw new NotImplementedException();
        }
    }
}
